package com.dailyblue.example.project.bookstore.book.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dailyblue.example.project.bookstore.book.feign.CategoryFeign;
import com.dailyblue.example.project.bookstore.book.service.BookService;
import com.dailyblue.example.project.bookstore.commons.bean.Book;
import com.dailyblue.example.project.bookstore.commons.bean.Category;
import com.dailyblue.example.project.bookstore.commons.mapper.BookMapper;
import com.dailyblue.example.project.bookstore.commons.util.JsonResult;
import com.dailyblue.example.project.bookstore.commons.util.ResultTool;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService {
    @Resource
    private CategoryFeign categoryFeign;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    // list中图书的key
    private static final String BOOK_LIST_KEY = "BOOK_LIST";
    // 图书在redis中每个key的前缀
    private static final String PREFIX_BOOK_KEY = "PREFIX_BOOK_";

    private static final String BOOK_PAGE_INFO = "BOOK_PAGE_INFO";

    private ValueOperations<String, String> operations_values;
    private ListOperations<String, String> operations_list;

    @Override
    public JsonResult findBook(int page) {
        log.info("开始进行分页查询图书操作，查询第{}页图书", page);
        operations_values = stringRedisTemplate.opsForValue();
        operations_list = stringRedisTemplate.opsForList();
        int begin = (page - 1) * 10;
        int end = page * 10 - 1;
        // 校验这一页在redis中是否存在
        List<String> range = operations_list.range(BOOK_LIST_KEY, begin, end);
        log.info("range:{}", range);
        // 不存在
        if (range == null || range.size() == 0) {
            log.info("图书从来没有存放到redis中");
            JsonResult<PageInfo<Book>> result = findByPage(page);
            setRedisByMySql(result, page);
            return result;
        }
        // 判断当前页是否是null
        for (int i = begin; i <= end; i++) {
            String s = range.get(i);
            if (s == null || "null".equals(s)) {
                log.info("图书在当前页没有存放到redis中");
                JsonResult<PageInfo<Book>> result = findByPage(page);
                setRedisByMySql(result, page);
                return result;
            }
        }
        log.info("{}页信息在redis中存在", page);
        List<Book> list = new ArrayList<>();
        range.forEach(e -> {
            // 获取到id
            String id = e.replace(PREFIX_BOOK_KEY, "");
            // redis中获取对应key的值
            String value = operations_values.get(e);
            Book book = JSONArray.parseObject(value, Book.class);
            list.add(book);
        });
        String bpi = operations_values.get(BOOK_PAGE_INFO);
        log.info("bpi:{}", bpi);
        PageInfo<Book> pageInfo = JSONArray.parseObject(bpi, PageInfo.class);
        pageInfo.setList(list);
        pageInfo.setPageNum(page);
        return ResultTool.success(pageInfo);
    }


    private JsonResult findByPage(int page) {
        PageHelper.startPage(page, 10);
        List<Book> list = list();
        // 拆箱装箱 给分类赋值
        list.forEach(e -> {
            String id = e.getCategoryId();
            JsonResult<Category> result = categoryFeign.find(id);
            e.setCategory(result.getData());
        });
        PageInfo<Book> pageInfo = new PageInfo<>(list);
        return ResultTool.success(pageInfo);
    }

    // 将数据存放到redis中
    private void setRedisByMySql(JsonResult<PageInfo<Book>> result, int page) {
        log.info("开始存放到list中");
        // 存放到value中
        List<Book> list = result.getData().getList();
        long total = result.getData().getTotal();
        log.info("list:{},total:{}", list, total);
        list.forEach(e -> operations_values.set(PREFIX_BOOK_KEY + e.getId(), JSONArray.toJSONString(e)));
        log.info("开始存放到string中");
        // 存放到list中
        Long size = operations_list.size(BOOK_LIST_KEY);
        if (size == 0) {
            for (int i = 0; i < total; i++) {
                operations_list.rightPush(BOOK_LIST_KEY, "null");
            }
        }
        int begin = (page - 1) * 10;
        int end = page * 10;
        log.info("size:{},begin:{},end:{}", size, begin, end);
        int index = 0;
        for (int i = begin; i < end; i++) {
            operations_list.set(BOOK_LIST_KEY, i, PREFIX_BOOK_KEY + list.get(index).getId());
            index++;
        }
        // 存放了pageInfo
        operations_values.set(BOOK_PAGE_INFO, JSONArray.toJSONString(result.getData()));
    }

    @Override
    public JsonResult removeBook(String id) {
        boolean b = removeById(id);
        if (b) {
            operations_values = stringRedisTemplate.opsForValue();
            operations_list = stringRedisTemplate.opsForList();
            operations_list.remove(BOOK_LIST_KEY, 1, PREFIX_BOOK_KEY + id);
            stringRedisTemplate.delete(PREFIX_BOOK_KEY + id);
        }
        return b ? ResultTool.success("删除成功") : ResultTool.fail(501, "删除失败");
    }

    @Override
    public JsonResult updateStatus(String id, int status) {
        UpdateWrapper<Book> wrapper = new UpdateWrapper<>();
        wrapper.eq("id", id);
        wrapper.set("status", status);
        boolean b = update(wrapper);
        if (b) {
            operations_values = stringRedisTemplate.opsForValue();
            String json = operations_values.get(PREFIX_BOOK_KEY + id);
            Book book = JSONArray.parseObject(json, Book.class);
            book.setStatus(status);
            operations_values.setIfPresent(PREFIX_BOOK_KEY + id, JSONArray.toJSONString(book));
        }
        return b ? ResultTool.success("修改状态成功") : ResultTool.fail(501, "修改状态失败");
    }

    @Override
    public JsonResult findBookByName(String name, int page) {
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.like("name", name)
                .or()
                .like("author", name)
                .or()
                .like("publisher", name);
        PageHelper.startPage(page, 10);
        List<Book> list = list(wrapper);
        log.info("查询出来的集合是：{}", list);
        // 拆箱装箱 给分类赋值
        list.forEach(e -> {
            log.info("--------------------------------------->");
            log.info("根据categoryID:{}", e.getCategoryId());
            JsonResult<Category> result = categoryFeign.find(e.getCategoryId());
            log.info("内容是：{}",result);
            e.setCategory(result.getData());
            // 书名、作者或出版社搜索文字高亮显示
            /*String replace = "<span style='color:red;'>" + name + "</span>";
            String bookName = e.getName();
            String publisher = e.getPublisher();
            String author = e.getAuthor();
            e.setName(bookName.replace(name, replace));
            e.setAuthor(author.replace(name, replace));
            e.setPublisher(publisher.replace(name, replace));*/
        });
        PageInfo<Book> pageInfo = new PageInfo<>(list);
        return ResultTool.success(pageInfo);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public JsonResult saveBook(Book book) {
        boolean b = save(book);
        if (b) {
            operations_list = stringRedisTemplate.opsForList();
            operations_list.leftPush(BOOK_LIST_KEY, "null");
        }
        return b ? ResultTool.success("添加成功") : ResultTool.fail(503, "添加失败");
    }

    @Override
    public JsonResult findAllPublisher() {
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.select("distinct publisher");
        List<Book> list = list(wrapper);
        List<String> publishers = new ArrayList<>();
        list.forEach(e -> {
            publishers.add(e.getPublisher());
        });
        return ResultTool.success(publishers);
    }

    // 新鲜好书 5本
    @Cacheable(cacheNames = "findFreshAndGoodBooksLimit", key = "0")
    public JsonResult findFreshAndGoodBooksLimit() {
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("create_time");
        wrapper.last(" limit 5");
        List<Book> list = list(wrapper);
        return ResultTool.success(list);
    }

    // 所有新鲜好书
    @Cacheable(cacheNames = "findFreshAndGoodBooks", key = "#page")
    public JsonResult findFreshAndGoodBooks(int page) {
        PageHelper.startPage(page, 10);
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("create_time");
        List<Book> list = list(wrapper);
        PageInfo<Book> pageInfo = new PageInfo<>(list);
        return ResultTool.success(pageInfo);
    }

    @Cacheable(cacheNames = "findFreshAndGoodBooks", key = "#name+':'+#page")
    public JsonResult searchFreshAndGoodBooks(String name, int page) {
        PageHelper.startPage(page, 10);
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.like("name", name);
        wrapper.orderByAsc("create_time");
        List<Book> list = list(wrapper);
        PageInfo<Book> pageInfo = new PageInfo<>(list);
        return ResultTool.success(pageInfo);
    }


    // 低价 4本
    @Cacheable(cacheNames = "findLowPriceBooksLimit", key = "0")
    public JsonResult findLowPriceBooksLimit() {
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("price");
        wrapper.last(" limit 4");
        List<Book> list = list(wrapper);
        return ResultTool.success(list);
    }

    @Cacheable(cacheNames = "findLowPriceBooks", key = "#page")
    public JsonResult findLowPriceBooks(int page) {
        PageHelper.startPage(page, 10);
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.orderByAsc("price");
        List<Book> list = list(wrapper);
        PageInfo<Book> pageInfo = new PageInfo<>(list);
        return ResultTool.success(pageInfo);
    }

    @Cacheable(cacheNames = "searchLowPriceBooks", key = "#name+':'+#page")
    public JsonResult searchLowPriceBooks(String name, int page) {
        PageHelper.startPage(page, 10);
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.like("name", name);
        wrapper.orderByAsc("price");
        List<Book> list = list(wrapper);
        PageInfo<Book> pageInfo = new PageInfo<>(list);
        return ResultTool.success(pageInfo);
    }

    @Cacheable(cacheNames = "findBooksByCategoryId", key = "#id+':'+#page")
    public JsonResult findBooksByCategoryId(String id, int page) {
        PageHelper.startPage(page, 10);
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.eq("category_id", id);
        List<Book> list = list(wrapper);
        PageInfo<Book> pageInfo = new PageInfo<>(list);
        return ResultTool.success(pageInfo);
    }

    @Cacheable(cacheNames = "searchCategoryIdBooks", key = "#id+':'+#name+':'+#page")
    public JsonResult searchCategoryIdBooks(String id, String name, int page) {
        PageHelper.startPage(page, 10);
        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.eq("category_id", id);
        wrapper.like("name", name);
        List<Book> list = list(wrapper);
        PageInfo<Book> pageInfo = new PageInfo<>(list);
        return ResultTool.success(pageInfo);
    }

    @Cacheable(cacheNames = "findBookById", key = "#id")
    public JsonResult findBookById(String id) {
        Book book = getBaseMapper().selectById(id);
        return ResultTool.success(book);
    }
}
